OkHttp的请求过程
在上一篇中我们大概了解了OkHttp大体的执行过程,本篇将进一步介绍OkHttp请求的具体过程,这设计到部分拦截器的具体细节。
####建立连接
关于内置的拦截器将会在其他篇章中分别做介绍,这里只介绍涉及到具体的Http请求过程的拦截器,它们是ConnectInterceptor和CallServerInterceptor分别负责连接的建立和Http请求的发起。我们先看ConnectInterceptor
1 | public final class ConnectInterceptor implements Interceptor { |
ConnectInterceptor的最终目的是和服务器建立连接,它首先从拦截器链中得到的StreamAllocation,但是拦截器链在初始时streamAllocation并未创建,那么它是何时创建,在哪里创建呢?可以确定的是它一定是在拦截器链ConnectInterceptor之前的拦截器中进行初始化的,通过寻找发现它是在RetryAndFollowUpInterceptor中创建的。
1 | public Response intercept(Chain chain) throws IOException { |
这个StreamAllocation到底是做什么的呢?我们看看它的具体实现
1 | /** |
从注释中我们可以了解到StreamAllocation的作用,它负责协调Connections,Streams以及Calls三者的关系,Connnections是代表了连接远程服务的物理socket连接,Streams是依赖于Connnections逻辑上的Http Request/Response请求对。每个connections都有它的分配上限,这个决定了connections可以并发支持的streams数目,Calls是streams逻辑上的序列,它实际上是一组Request。清楚了这些,再看其构造方法,其中ConnectionPool想必就是负责物理连接的,Address描述了我们需要连接的远端服务,实际上它是通过url来得到的。
接下来要为一次请求创建一个Stream,这个Stream是逻辑上的Request/Response对,通过StreamAllocation的newStream方法创建。
1 | public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) { |
newStream方法为stream找到一个可用的连接,然后通过连接的newCodec得到一个HttpCodec,它负责对Http的Request进行编码以及对Response进行解码。接着再看findHealthyConnection
1 | private RealConnection findHealthyConnection(int connectTimeout, int readTimeout, |
通过一个while循环直到找到一个health RealConnection.
1 | private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout, |
findConnection负责从连接池中取出一个匹配的RealConnection,如果未找到就创建一个新的,并通过connect建立和远端服务的连接。这个是通过RealConnection的connect方法来实现的。
1 | public final class RealConnection extends Http2Connection.Listener implements Connection { |
1 | public void connect( |
connect负责连接的建立过程,这里通过while循环来创建连接,保证连接能够创建成功,在循环中首先根据requiresTunnel判读是否创建隧道连接,它需要满足
1 | public boolean requiresTunnel() { |
即对于设置了HTTP代理,且安全的连接。这时候需要请求HTTP代理建立一个到目标HTTP服务器的隧道连接,客户端和HTTP代理建立TCP连接。这里我们只关心不设置代理的情况,即创建连接通过connectSocket来完成。
1 | /** Does all the work necessary to build a full HTTP or HTTPS connection on a raw socket. */ |
这里我们看AndroidPlatform的connectSocket
1 | public void connectSocket(Socket socket, InetSocketAddress address, |
connectSocket实际就是通过创建的rawSocket来建立TCP连接,连接建立成功后,需要进一步建立协议,它主要为数据的加密传输做一些初始化,比如TLS握手,HTTP/2的协议协商等。
1 | private void establishProtocol(ConnectionSpecSelector connectionSpecSelector) throws IOException { |
未配置sslScoketFactor就默认是HTTP/1.1,这时候socket和rawSocket是同一个,否则需要为安全请求建立TLS连接。
1 | private void connectTls(ConnectionSpecSelector connectionSpecSelector) throws IOException { |
TLS连接是对原始的TCP连接的一个封装,以提供TLS握手,及数据收发过程中的加密解密等功能。它通过SSLSocket来描述,建立一个TLS连接的步骤为:
- 配置SSLSocket
- 配置扩展参数
- 开始握手
- 获取握手信息
- 验证证书信息
- 创建用于执行IO的BufferedSource和BufferedSink等,并保存握手信息及所选择的协议
建立好连接后,在StreamAllocation的newStream中接下来就是创建HttpCodec返回,HttpCodec负责Request和Response的编解码。这里我们看看它的创建过程
1 | public HttpCodec newCodec( |
newCodec会根据HTTP的版本来创建Http2Codec或者Http1Codec对象。
###发起请求
建立好连接后,就需要发起http请求了,这个是在CallServerInterceptor这个拦截器中进行的,我们看看它的实现。
1 | public Response intercept(Chain chain) throws IOException { |
可以看到整个Http请求和响应的过程是通过HttpCodec来进行操作的,首先通过writeRequestHeaders发起http请求,然后创建Response.Builder对象,负责接收响应。